package com.yang.utils;

import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;


@Slf4j
public class BeanUtil {
	
	/**
	 * 给指定的bean的某个字段赋值 ,若 bean中不含有该字段则跳过
	 * 此处若该字段不是String类型，则进行类型转换，此处需要该类型有个参数为String类型的构造方法
	 * @param bean
	 * @param fieldName
	 * @param value
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void setProperty(Object bean,String fieldName,String value) throws Exception{
		PropertyDescriptor propDesc=getPropertyDescriptor(fieldName,bean.getClass());
		if(propDesc==null){
			return;
		}
		Class clazz=propDesc.getPropertyType();
		Method setMethod=propDesc.getWriteMethod();
		//若该字段不是String类型，则进行类型转换，此处需要该类型有个参数为String类型的构造方法
		if(String.class!=clazz){
			setMethod.invoke(bean, clazz.getConstructor(String.class).newInstance(value));
		}else{
			setMethod.invoke(bean, value);
		}
	}
	
	@SuppressWarnings("rawtypes")
	public static PropertyDescriptor getPropertyDescriptor(String fieldName,Class clazz){
		try {
			return new PropertyDescriptor(fieldName,clazz);
		} catch (Exception e) {
			return null;
		}
	}

    /**
     *将非Map类型的object的所有字段全部转成String类型
     * @param bean
     * @return
     * @throws IllegalAccessException
     */
    public static Map fieldToString(Object bean) throws IllegalAccessException {
        Map result=new HashMap<>();
        Class clazz=bean.getClass();
        List<Field> fields=getFields(clazz);
        for(Field field:fields){
            field.setAccessible(true);
            String name=field.getName();
            Object fieldValue=field.get(bean);
            if(fieldValue==null){
                result.put(name,fieldValue);
            }else if(fieldValue instanceof Logger){
            	continue;
			}else if(isBaseData(fieldValue)){
            	if(Date.class.isInstance(fieldValue)){
            		result.put(name, ((Date)fieldValue).getTime()+"");
            	}else{ 
            		result.put(name,fieldValue.toString());
            	}
            }else if(fieldValue instanceof ImmutableMap){/*不可变map跳过*/
				continue;
			}else if(fieldValue instanceof Map){
                StringUtil.allAttributesToString((Map<String, Object>) fieldValue);
                result.put(name,fieldValue);
            }else if(fieldValue instanceof Enum){
                /*枚举类型，强转成String,使用name属性*/
                result.put(name,((Enum)fieldValue).name());
            }else if(fieldValue instanceof Collection){
                result.put(name, StringUtil.allToString((Collection)fieldValue));
            }else{
                result.put(name,fieldToString(fieldValue));
            }
        }
        return result;
    }
    
    
    public static List<Field> getFields(Class clazz){
    	Class superClazzClass=clazz.getSuperclass();
    	List<Field> fields=new ArrayList<>();
        if(superClazzClass!=null&&superClazzClass!= Serializable.class){
    		fields.addAll(getFields(superClazzClass));
    	}
    	fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
    	return fields;
    }

    /**
     * 判断一个类是否为基本数据类型。
     * @param clazz 要判断的类。
     * @return true 表示为基本数据类型。
     */
    public static boolean isBaseDataType(Class clazz)
    {
        return
        (
            clazz.equals(String.class) ||
            clazz.equals(Integer.class)||
            clazz.equals(Byte.class) ||
            clazz.equals(Long.class) ||
            clazz.equals(Double.class) ||
            clazz.equals(Float.class) ||
            clazz.equals(Character.class) ||
            clazz.equals(Short.class) ||
            clazz.equals(BigDecimal.class) ||
            clazz.equals(BigInteger.class) ||
            clazz.equals(Boolean.class) ||
            clazz.equals(Date.class) ||
            clazz.isPrimitive()
        );
    }

	/**
	 * 判断obj是否为基本数据类型。
	 * @param value 要判断的类。
	 * @return true 表示为基本数据类型。
	 */
	public static boolean isBaseData(Object value)
	{
		return
		(
			value instanceof String ||
			value instanceof Integer||
			value instanceof Byte ||
			value instanceof Long ||
			value instanceof Double ||
			value instanceof Float ||
			value instanceof Character ||
			value instanceof Short ||
			value instanceof BigDecimal ||
			value instanceof BigInteger ||
			value instanceof Boolean ||
			value instanceof Date
		);
	}





	/**
	 * 获取带泛型的实际类型
	 * @param type
	 */
	public static List<Class> getActualType(Type type) {
		List<Class> result=new ArrayList<>();
		// 参数化类型:也就是泛型
		if ( type instanceof ParameterizedType) {
			Type[] typeArguments = ((ParameterizedType)type).getActualTypeArguments();
			for (int i = 0; i < typeArguments.length; i++) {
				// 泛型的实际类型，即实际存在的类型
				if(typeArguments[i] instanceof Class){
					result.add((Class) typeArguments[i]);
				}
			}
			// 参数化类型数组或类型变量数组
		} else if ( type instanceof GenericArrayType) {
			Type componentType = ((GenericArrayType)type).getGenericComponentType();
			// 类型变量
			if(componentType instanceof TypeVariable){
				return null;
			}
			// 参数化类型，参数化类型数组或类型变量数组
			// 参数化类型数组或类型变量数组也可以是多维的数组，getGenericComponentType()方法仅仅是去掉最右边的[]
			else {
				// 递归调用方法自身
				result.addAll(getActualType(componentType));
			}
		}else{
			return null;
		}
		return result;
	}

	/**
	 * 获取object 下的一个
	 * @param obj
	 * @param fieldName
	 * @return
	 */
	public static String getStringField(Object obj ,String fieldName){
		try {
			return BeanUtils.getSimpleProperty(obj,fieldName);
		} catch (Exception e) {
			log.error("thridPark not contains uid",e);
			return "";
		}
	}

}
